package com.bhb.android.module.school.widgets

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Resources
import android.graphics.*
import android.util.AttributeSet
import android.util.SparseIntArray
import android.view.View
import androidx.annotation.DrawableRes
import java.util.*

/**
 * Describe: 学堂用户活跃
 *
 *
 * @author liaohailong
 * Time: 2020/9/2 14:40
 */
class SchoolUserView(context: Context, attr: AttributeSet?, defInt: Int) :
        View(context, attr, defInt) {
    constructor(context: Context, attr: AttributeSet?) : this(context, attr, 0)
    constructor(context: Context) : this(context, null)

    private val maxShowCount = 3
    private var modelWidth = 0
    private val allDataModels = mutableListOf<SingleUserModel>()
    private val moreModel = SingleUserModel(-1, "", context, Color.YELLOW, this)
    private val leftAnchorList: SparseIntArray = SparseIntArray()
    private var leftAnchor: Int = 0

    /**
     * 数据间隔 以宽度为参考
     */
    private val dataSpanPercent = 0.23f

    /**
     * 数据模块与更多模块之间的间隙
     */
    private val moreSpanPercent = 0.32f

    /**
     * 不可见的数据模型存放集合
     * - 按顺序 表头为下一个取出的 新加入的放在表尾
     */
    private val invisibleList = LinkedList<SingleUserModel>()

    /**
     * 可见的数据模型
     */
    private val visibleList = LinkedList<SingleUserModel>()

    /**
     * 图片资源加载回调
     */
    private var imageLoadCallback: OnImageLoadCallback? = null

    /**
     * 更多模型的图片资源id
     */
    @DrawableRes
    private var moreModelResId: Int = 0

    fun setData(vararg avatar: String): SchoolUserView {
        val oldSize = allDataModels.size
        allDataModels.forEach { it.release() }
        allDataModels.clear()

        avatar.forEach {
            val index = avatar.indexOf(it)
            allDataModels.add(
                    SingleUserModel(
                            index,
                            it,
                            context,
                            Color.parseColor("#F2F2F2"),
                            this
                    )
            )
        }

        if (allDataModels.size != oldSize)
            requestLayout()
        else
            invalidate()

        return this
    }

    fun setImageLoadCallback(imageLoadCallback: OnImageLoadCallback): SchoolUserView {
        this@SchoolUserView.imageLoadCallback = imageLoadCallback
        return this
    }

    fun setMoreResourceId(@DrawableRes resId: Int): SchoolUserView {
        moreModelResId = resId
        return this
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val width = prepareModels(heightMeasureSpec)
        if (width <= 0) {
            val makeMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY)
            super.onMeasure(makeMeasureSpec, heightMeasureSpec)
        } else {
            val makeMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY)
            super.onMeasure(makeMeasureSpec, heightMeasureSpec)
        }

        calculateModelPosition()
        sortModels()
        loadImage()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        modelWidth = h
    }


    private fun prepareModels(heightMeasureSpec: Int): Int {
        // 没有数据，没有宽度
        if (allDataModels.isEmpty()) return 0

        // 正方形边长
        val length = MeasureSpec.getSize(heightMeasureSpec)
        // 给模型设置尺寸信息
        allDataModels.forEach { it.setLength(length) }
        moreModel.setLength(length)
        // 头像显示个数
        val count = if (allDataModels.size > maxShowCount) maxShowCount + 1 else allDataModels.size
        // 数据模块之间的间距
        val dataSpan = dataSpanPercent * length
        // 数据模块与更多模块间距
        val moreSpan = moreSpanPercent * length
        // 计算最大宽度
        return when {
            // 只有一个数据
            count == 1 -> {
                length
            }
            // 1~maxShowCount 数据时
            count <= maxShowCount -> {
                val totalWidth = length * count
                val spanWidth = dataSpan * (count - 1)
                (totalWidth - spanWidth).toInt()
            }
            // 大于maxShowCount时 左边maxShowCount个数据模型 右边一个更多模型
            else -> {
                val totalWidth = length * count
                val spanWidth = dataSpan * (count - 2) + moreSpan
                (totalWidth - spanWidth).toInt()
            }
        }
    }

    /**
     * 计算数据模型位置
     */
    private fun calculateModelPosition() {
        // 所有数据模型右边的锚点
        var rightPoint = if (allDataModels.size > maxShowCount) {
            // 存在更多模块
            moreModel.needDraw = true
            // 数据模块与更多模块间距
            val moreSpan = moreSpanPercent * moreModel.width
            // 最右边布局位置
            (measuredWidth - moreModel.width + moreSpan).toInt()
        } else {
            // 不存在更多模块
            moreModel.needDraw = false
            // 最右边位置
            measuredWidth
        }

        // 给模型设置位置
        if (moreModel.needDraw) {
            moreModel.left = measuredWidth - moreModel.width
        }

        // 超出最大显示个数时
        if (allDataModels.size > maxShowCount) {
            // 从右边往左边布局
            for (dataModel in allDataModels) {
                val index = allDataModels.indexOf(dataModel)
                // 数据模块之间的间距
                val dataSpan = dataSpanPercent * dataModel.width
                val left = if (index == 0) {
                    rightPoint - dataModel.width
                } else (rightPoint + dataSpan - dataModel.width).toInt()
                // 更新右锚点
                rightPoint = left
                // 确定初始化数据模型left锚点
                dataModel.left = left
                // 保存每个位置的左锚点，后续动画使用
                leftAnchorList.put(index, left)
                // 右边锚点
                if (index == maxShowCount) leftAnchor = left
            }
        }
        // 小于等于最大显示个数时 - 不执行动画
        else {
            for (dataModel in allDataModels) {
                val index = allDataModels.indexOf(dataModel)
                // 数据模块之间的间距
                val dataSpan = dataSpanPercent * dataModel.width
                val left = if (index == 0) {
                    rightPoint - dataModel.width
                } else (rightPoint + dataSpan - dataModel.width).toInt()
                // 更新右锚点
                rightPoint = left
                // 确定初始化数据模型left锚点
                dataModel.left = left
            }
        }
    }

    /**
     * 将所有数据模型分类
     * - 可见的
     * - 不可见的
     */
    private fun sortModels() {
        invisibleList.clear()
        visibleList.clear()
        // 布局从右往左
        for (dataModel in allDataModels) {
            if (dataModel.left < 0) {
                dataModel.needDraw = false
                invisibleList.add(dataModel)
            } else {
                dataModel.needDraw = true
                visibleList.add(dataModel)
            }
        }
    }

    /**
     * 模型加载图片
     */
    private fun loadImage() {
        // 数据模型的图片是线上url
        allDataModels.forEach {
            val url = it.avatar
            val width = it.width
            val height = it.height
            imageLoadCallback?.onLoadImage(url, width, height, it.imageReceiver)
        }
        // 更多模型的图片是本地资源id
        val width = moreModel.width
        val height = moreModel.height
        val bitmap =
                decodeSampledBitmapFromResource(resources, moreModelResId, width, height)
        bitmap?.apply { moreModel.bitmap = this }
    }

    private fun decodeSampledBitmapFromResource(
            res: Resources?, resId: Int,
            reqWidth: Int, reqHeight: Int
    ): Bitmap? {
        // 第一次解析将inJustDecodeBounds设置为true，来获取图片大小
        val options = BitmapFactory.Options()
        options.inJustDecodeBounds = true
        BitmapFactory.decodeResource(res, resId, options)
        // 调用上面定义的方法计算inSampleSize值
        options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight)
        // 使用获取到的inSampleSize值再次解析图片
        options.inJustDecodeBounds = false
        return BitmapFactory.decodeResource(res, resId, options)
    }

    private fun calculateInSampleSize(
            options: BitmapFactory.Options,
            reqWidth: Int, reqHeight: Int
    ): Int {
        // 源图片的高度和宽度
        val height = options.outHeight
        val width = options.outWidth
        var inSampleSize = 1
        if (height > reqHeight || width > reqWidth) {
            // 计算出实际宽高和目标宽高的比率
            val heightRatio = Math.round(height.toFloat() / reqHeight.toFloat())
            val widthRatio = Math.round(width.toFloat() / reqWidth.toFloat())
            // 选择宽和高中最小的比率作为inSampleSize的值，这样可以保证最终图片的宽和高
            // 一定都会大于等于目标的宽和高。
            inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio
        }
        return inSampleSize
    }


    private val animDuration = 400L
    private val animDurationDelay = 600L
    var animRunning = false
    private val updateListener: ValueAnimator.AnimatorUpdateListener =
            ValueAnimator.AnimatorUpdateListener {
                var progress = it.animatedValue as Float
                progress = Math.min(1.0f, progress)
                progress = Math.max(0.0f, progress)
                updateModels(progress)
            }
    private val animListener: Animator.AnimatorListener = object : AnimatorListenerAdapter() {
        override fun onAnimationStart(animation: Animator?) {
            super.onAnimationStart(animation)
            updateModels(0.0f)
            onAnimStart()
        }

        override fun onAnimationEnd(animation: Animator?) {
            super.onAnimationEnd(animation)
            updateModels(1.0f)
            onAnimEnd()
            if (animRunning) {
                removeCallbacks(startAnimRunnable)
                postDelayed(startAnimRunnable, animDurationDelay)
            }
        }
    }
    private var anim: ValueAnimator? = null
    private val startAnimRunnable = { startAnim() }

    fun start() {
        if (animRunning) return
        animRunning = true
        removeCallbacks(startAnimRunnable)
        post(startAnimRunnable)
    }

    fun stop() {
        animRunning = false
        stopAnim()
        removeCallbacks(startAnimRunnable)
    }

    /**
     * 开始动画
     */
    private fun startAnim() {
        // 动画执行限制
        if (allDataModels.size <= maxShowCount) return

        // 结束老动画
        stopAnim()

        // 开启动画 使用0.0~1.0的进度值
        anim = ValueAnimator.ofFloat(0.0f, 1.0f)
        anim?.apply {
            duration = animDuration
            addUpdateListener(updateListener)
            addListener(animListener)
        }
        prepareStatus()
        anim?.start()
    }

    /**
     * 初始化数据模型状态
     */
    private fun prepareStatus() {
        visibleList.forEach { it.status = Status.VISIBLE }
        invisibleList.forEach { it.status = Status.INVISIBLE }
    }

    /**
     * 一组动画开始
     */
    private fun onAnimStart() {
        val enter = invisibleList.pollFirst()
        enter?.apply {
            needDraw = true
            visibleList.addLast(this)
        }
        visibleList.forEach {
            // 重新设置左锚点
            val index = visibleList.indexOf(it)
            val key = leftAnchorList.keyAt(index)
            val left = leftAnchorList.get(key)
            it.left = left
            it.status = Status.VISIBLE
            it.reset()
        }
        visibleList.last.status = Status.FADE_IN
        visibleList.first.status = Status.FADE_OUT
    }

    /**
     * 一组动画结束
     */
    private fun onAnimEnd() {
        val leave = visibleList.pollFirst()
        leave?.apply {
            needDraw = false
            status = Status.INVISIBLE
            invisibleList.addLast(this)
        }
    }

    /**
     * 更新数据模型动画
     * @param progress 进度 [0.0~1.0]
     */
    private fun updateModels(progress: Float) {
        visibleList.forEach {
            val length = (modelWidth - modelWidth * dataSpanPercent).toInt()
            it.updateProgress(progress, length)
        }
        invalidate()
    }

    private fun stopAnim() {
        anim?.apply {
            removeUpdateListener(updateListener)
            removeListener(animListener)
            cancel()
        }
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas?.apply {
            // 控制范围别绘制出去了
            clipRect(leftAnchor.toFloat(), 0F, measuredWidth.toFloat(), measuredHeight.toFloat())
            // 绘制更多模型
            moreModel.draw(this)
            // 绘制数据模型
            visibleList.forEach { it.draw(this) }
        }
    }


    class SingleUserModel(
            private val index: Int = 0,
            val avatar: String,
            private val context: Context,
            private val color: Int,
            private val view: View
    ) {
        var width: Int = 0
        var height: Int = 0
        var left: Int = 0
        var needDraw = true
        var bitmap: Bitmap? = null
        val imageReceiver: ImageReceiver = ImageReceiver(object :
                ImageReceiver.OnImageReceivedListener {
            override fun onReceived(bitmap: Bitmap) {
                this@SingleUserModel.bitmap = bitmap
                view.postInvalidate()
            }
        })

        // 绘制相关工具类
        private val clipPath: Path = Path()
        private val contentPaint: Paint = Paint().apply {
            isAntiAlias = true
            style = Paint.Style.FILL_AND_STROKE
        }
        private val circlePaint: Paint = Paint().apply {
            isAntiAlias = true
            style = Paint.Style.STROKE
            color = Color.WHITE
            // 线宽定为一个dp
            strokeWidth = context.resources.displayMetrics.density
        }
        private val bitmapSrc = Rect()
        private val bitmapDst = Rect()

        // 动画相关
        var status: Status = Status.INVISIBLE
        private var translate: Int = 0
        private var alpha: Int = 255
        private var scale: Float = 1.0f

        fun setLength(length: Int) {
            width = length
            height = length
        }

        /**
         * @param progress 进度 [0.0~1.0]
         */
        fun updateProgress(progress: Float, length: Int) {
            translate = (progress * length).toInt()
            alpha = when (status) {
                Status.FADE_OUT -> (255 * (1.0 - progress)).toInt()
                Status.FADE_IN -> (255 * progress).toInt()
                else -> 255
            }
            scale = when (status) {
                Status.FADE_OUT -> 1.0f - progress
                else -> 1.0f
            }
        }

        /**
         * 动画执行完毕后，同步位置
         */
        fun reset() {
            translate = 0
            alpha = 255
        }

        fun draw(canvas: Canvas) {
            if (!needDraw) return
            canvas.save()
            // 裁剪出一个原型
            clipPath.reset()
            val startX = left + translate
            val cx = startX + width / 2f
            val cy = height / 2f
            val radius = (width / 2f) * scale
            clipPath.addCircle(cx, cy, radius, Path.Direction.CW)
            canvas.clipPath(clipPath)
            // 绘制内容
            contentPaint.color = color
            contentPaint.alpha = alpha
            val leftX = startX.toFloat()
            val rightX = startX.toFloat() + width.toFloat()
            // 绘制背景
            if (index >= 0)
                canvas.drawRect(
                        leftX,
                        0F,
                        rightX,
                        height.toFloat(),
                        contentPaint
                )
            // 绘制位图
            bitmap?.apply {
                // 计算出centerCrop偏移量
                bitmapSrc.set(0, 0, width, height)
                bitmapDst.set(leftX.toInt(), 0, rightX.toInt(), this@SingleUserModel.height)
                centerCrop(bitmapSrc, bitmapDst)
                canvas.drawBitmap(this, bitmapSrc, bitmapDst, contentPaint)
            }
            // 添加一个圆环线
            if (index >= 0) {
                circlePaint.alpha = alpha
                canvas.drawPath(clipPath, circlePaint)
            }
            canvas.restore()
        }

        private fun centerCrop(src: Rect, dst: Rect) {
            val srcWidth = src.width()
            val srcHeight = src.height()
            val dstWidth = dst.width()
            val dstHeight = dst.height()

            // 先把资源图片尺寸中最短的那一边，变成和绘制区域最长的变一样大
            val scaleW = dstWidth.toFloat() / srcWidth
            val scaleH = dstHeight.toFloat() / srcHeight
            val scale = Math.max(scaleW, scaleH)

            // 计算偏移值
            val scaledSrcWidth = srcWidth * scale
            val scaledSrcHeight = srcHeight * scale

            // 找出那一条边超出了绘制区域
            var hOffset = 0F
            var vOffset = 0F
            when {
                scaledSrcWidth > dstWidth -> {
                    hOffset = (scaledSrcWidth - dstWidth) / 2f
                }
                scaledSrcHeight > dstHeight -> {
                    vOffset = (scaledSrcHeight - dstHeight) / 2f
                }
                else -> {
                    // 碰巧都是一样的宽高比例
                    hOffset = 0F
                    vOffset = 0F
                }
            }

            src.left += hOffset.toInt()
            src.right -= hOffset.toInt()
            src.top += vOffset.toInt()
            src.bottom -= vOffset.toInt()
        }


        fun release() {

        }
    }

    /**
     * 动画执行状态
     */
    enum class Status {
        /**
         * 完全不可见
         */
        INVISIBLE,

        /**
         * 渐隐进入
         */
        FADE_IN,

        /**
         * 可见 - 动画时平移
         */
        VISIBLE,

        /**
         * 渐隐淡出
         */
        FADE_OUT
    }

    /**
     * 位图加载回调
     */
    interface OnImageLoadCallback {
        /**
         * 回调给外部调用，通知加载图片资源
         * @param avatar 图片url
         * @param width 期望的宽度
         * @param height 期望的高度
         * @param receiver 加载完成后回调 receiver.complete(bitmap)
         */
        fun onLoadImage(avatar: String, width: Int, height: Int, receiver: ImageReceiver)
    }

    class ImageReceiver(private val listener: OnImageReceivedListener) {

        fun complete(bitmap: Bitmap) {
            listener.onReceived(bitmap)
        }


        interface OnImageReceivedListener {
            fun onReceived(bitmap: Bitmap)
        }
    }
}